package hu.uszeged.inf.wlab.stunner.database;

import hu.uszeged.inf.wlab.stunner.database.dtos.BatteryInfoDTO;
import hu.uszeged.inf.wlab.stunner.database.dtos.DiscoveryDTO;
import hu.uszeged.inf.wlab.stunner.database.dtos.MobileNetInfoDTO;
import hu.uszeged.inf.wlab.stunner.database.dtos.WifiInfoDTO;
import hu.uszeged.inf.wlab.stunner.database.utils.DatabaseConstants;

import java.text.MessageFormat;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;

/**
 * Contract class which contains the entity definitions. Database interaction should always reference the definitions of this class.
 * 
 * @author szelezsant
 */
public final class STUNnerContract {
	/** The constant authority. */
	public static final String AUTHORITY = "hu.uszeged.inf.wlab.stunner.provider";
	/** Constant base Uri. */
	public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + STUNnerSqliteHelper.DATABASE_NAME);
	/** Constant for the base table mime type string. */
	public static final String BASE_TABLE_MIME_TYPE = "/vnd.hu.uszeged.inf.wlab.";

	/** Error message used when a given cursor is not correctly positioned. */
	private static final String CURSOR_POSITION_ERROR = "Cursor should be properly positioned!";

	/**
	 * Hidden constructor.
	 */
	private STUNnerContract() {

	}

	/**
	 * Discovery entity.
	 * 
	 * @author szelezsant
	 */
	public static final class Discovery implements BaseColumns {
		/** The constant content identifier. */
		public static final String TABLE_NAME = "discovery";
		/** The content {@link Uri} pointing to the current content. */
		public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(TABLE_NAME).build();
		/** The MIME type for list queries. */
		public static final String CONTENT_TYPE_DIR = ContentResolver.CURSOR_DIR_BASE_TYPE + BASE_TABLE_MIME_TYPE + TABLE_NAME;
		/** The MIME type for single item queries. */
		public static final String CONTENT_TYPE_ITEM = ContentResolver.CURSOR_ITEM_BASE_TYPE + BASE_TABLE_MIME_TYPE + TABLE_NAME;

		/** Column to store the timestamp in milliseconds, as an integer. */
		public static final String COLUMN_TIMESTAMP = "timestamp";
		/** Column to store the latitude as a real number. */
		public static final String COLUMN_LATITUDE = "latitude";
		/** Column to store the longitude as a real number. */
		public static final String COLUMN_LONGITUDE = "longitude";
		/** Column to store the formatted local IP address as text. */
		public static final String COLUMN_LOCAL_IP = "localip";
		/** Column to store the formatted public IP address as text. */
		public static final String COLUMN_PUBLIC_IP = "publicip";
		/** Column to store the discovery result code as an integer. */
		public static final String COLUMN_RESULT = "result";
		/** Column to indicate if the record is waiting for uploading. */
		public static final String COLUMN_PENDING = "pending";
		/** Column to store the bookmarked flag. */
		public static final String COLUMN_BOOKMARKED = "bookmarked";
		/** Column to store the battery information identifier. */
		public static final String COLUMN_BATTERY_INFO_ID = "batteryInfoId";
		/** Column to store the wifi information identifier. */
		public static final String COLUMN_WIFI_INFO_ID = "wifiInfoId";
		/** Column to store the mobile network information identifier. */
		public static final String COLUMN_MOBILE_INFO_ID = "mobileInfoId";

		/** The SQL command to create the table. */
		public static final String CREATE_TABLE = MessageFormat.format(DatabaseConstants.CREATE_TABLE_PATTERN, TABLE_NAME)
				+ "(" + _ID + " " + DatabaseConstants.INTEGER + " " + DatabaseConstants.PRIMARY_KEY + " " + DatabaseConstants.AUTOINCREMENT
				+ DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_TIMESTAMP + " " + DatabaseConstants.INTEGER + DatabaseConstants.NOT_NULL + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_LATITUDE + " " + DatabaseConstants.REAL + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_LONGITUDE + " " + DatabaseConstants.REAL + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_LOCAL_IP + " " + DatabaseConstants.TEXT + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_PUBLIC_IP + " " + DatabaseConstants.TEXT + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_PENDING + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_RESULT + " " + DatabaseConstants.INTEGER + DatabaseConstants.NOT_NULL + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_BOOKMARKED + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_BATTERY_INFO_ID + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_WIFI_INFO_ID + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_MOBILE_INFO_ID + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ MessageFormat.format(DatabaseConstants.FOREIGN_KEY_REFERENCES_PATTERN,
						new Object[] {COLUMN_BATTERY_INFO_ID, STUNnerContract.BatteryInfo.TABLE_NAME, STUNnerContract.BatteryInfo._ID})
				+ DatabaseConstants.COMMA_SEPARATOR
				+ MessageFormat.format(DatabaseConstants.FOREIGN_KEY_REFERENCES_PATTERN,
						new Object[] {COLUMN_WIFI_INFO_ID, STUNnerContract.WifiInfo.TABLE_NAME, STUNnerContract.WifiInfo._ID})
				+ DatabaseConstants.COMMA_SEPARATOR
				+ MessageFormat.format(DatabaseConstants.FOREIGN_KEY_REFERENCES_PATTERN,
						new Object[] {COLUMN_MOBILE_INFO_ID, STUNnerContract.MobileNetInfo.TABLE_NAME, STUNnerContract.MobileNetInfo._ID})
				+ ");";

		/**
		 * Hidden constructor.
		 */
		private Discovery() {
		}

		/**
		 * Creates a {@link ContentValues} object from the given {@link DiscoveryDTO} object.
		 * 
		 * @param discoveryDTO - the {@link DiscoveryDTO} object representing the discovery.
		 * @return {@link ContentValues} object filled with the attributes and values of the argument
		 */
		public static ContentValues createContentValues(final DiscoveryDTO discoveryDTO) {
			final ContentValues contentValues = new ContentValues();

			contentValues.put(COLUMN_LATITUDE, discoveryDTO.getLatitude());
			contentValues.put(COLUMN_LONGITUDE, discoveryDTO.getLongitude());
			contentValues.put(COLUMN_RESULT, discoveryDTO.getDiscoveryResultCode());
			contentValues.put(COLUMN_LOCAL_IP, discoveryDTO.getLocalIP());
			contentValues.put(COLUMN_PUBLIC_IP, discoveryDTO.getPublicIP());
			contentValues.put(COLUMN_TIMESTAMP, System.currentTimeMillis());
			contentValues.put(COLUMN_BOOKMARKED, discoveryDTO.isBookmarked());
			contentValues.put(COLUMN_BATTERY_INFO_ID, discoveryDTO.getBatteryInfoId());
			contentValues.put(COLUMN_WIFI_INFO_ID, discoveryDTO.getWifiInfoId());
			contentValues.put(COLUMN_MOBILE_INFO_ID, discoveryDTO.getMobileInfoId());
			contentValues.put(COLUMN_PENDING, discoveryDTO.isPending());

			return contentValues;
		}

		/**
		 * Creates the {@link DiscoveryDTO} object from the supplied cursor. The cursor should be properly positioned to the actual row.
		 * Else this method throws {@link IllegalStateException}.
		 * 
		 * @param cursor - the {@link Cursor} object
		 * @return {@link DiscoveryDTO} object
		 */
		public static DiscoveryDTO cursorToDTO(final Cursor cursor) {
			if (cursor.getCount() > 0 && !cursor.isBeforeFirst() && !cursor.isAfterLast()) {
				final DiscoveryDTO dto = new DiscoveryDTO();
				dto.setBookmarked(1 == cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_BOOKMARKED)));
				dto.setDiscoveryResultCode(cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_RESULT)));
				dto.setIdentifier(cursor.getLong(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery._ID)));
				dto.setLatitude(cursor.getDouble(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_LATITUDE)));
				dto.setLongitude(cursor.getDouble(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_LONGITUDE)));
				dto.setPublicIP(cursor.getString(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_PUBLIC_IP)));
				dto.setLocalIP(cursor.getString(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_LOCAL_IP)));
				dto.setTimeStamp(cursor.getLong(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_TIMESTAMP)));
				dto.setBatteryInfoId(cursor.getLong(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_BATTERY_INFO_ID)));
				dto.setMobileInfoId(cursor.getLong(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_MOBILE_INFO_ID)));
				dto.setWifiInfoId(cursor.getLong(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_WIFI_INFO_ID)));
				dto.setPending(1 == cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.Discovery.COLUMN_PENDING)));

				return dto;
			}
			throw new IllegalStateException(CURSOR_POSITION_ERROR);
		}
	}

	/**
	 * Table to store the battery information.
	 * 
	 * @author szelezsant
	 */
	public static final class BatteryInfo implements BaseColumns {
		/** The constant content identifier. */
		public static final String TABLE_NAME = "batteryinfo";
		/** The content {@link Uri} pointing to the current content. */
		public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(TABLE_NAME).build();
		/** The MIME type for list queries. */
		public static final String CONTENT_TYPE_DIR = ContentResolver.CURSOR_DIR_BASE_TYPE + BASE_TABLE_MIME_TYPE + TABLE_NAME;
		/** The MIME type for single item queries. */
		public static final String CONTENT_TYPE_ITEM = ContentResolver.CURSOR_ITEM_BASE_TYPE + BASE_TABLE_MIME_TYPE + TABLE_NAME;

		/** Column to store the charging state according to the {@link android.os.BatteryManager BatteryManager} constants. */
		public static final String COLUMN_CHARGING_STATE = "chargingState";
		/** Column to store the charge percentage. */
		public static final String COLUMN_PERCENTAGE = "percentage";
		/** Column to store the battery health. */
		public static final String COLUMN_HEALTH = "health";
		/** Column to store the temperature. */
		public static final String COLUMN_TEMPERATURE = "temperature";
		/** Column to store voltage. */
		public static final String COLUMN_VOLTAGE = "voltage";

		/** The SQL command to create the table. */
		public static final String CREATE_TABLE = MessageFormat.format(DatabaseConstants.CREATE_TABLE_PATTERN, TABLE_NAME)
				+ "(" + _ID + " " + DatabaseConstants.INTEGER + " " + DatabaseConstants.PRIMARY_KEY + " " + DatabaseConstants.AUTOINCREMENT
				+ DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_CHARGING_STATE + " " + DatabaseConstants.INTEGER + DatabaseConstants.NOT_NULL + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_PERCENTAGE + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_HEALTH + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_VOLTAGE + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_TEMPERATURE + " " + DatabaseConstants.INTEGER + ");";

		/**
		 * Hidden constructor.
		 */
		private BatteryInfo() {
		}

		/**
		 * Creates a {@link ContentValues} object from the given {@link BatteryInfoDTO} object.
		 * 
		 * @param batteryInfoDTO - the {@link BatteryInfoDTO} object representing the discovery.
		 * @return {@link ContentValues} object filled with the attributes and values of the argument
		 */
		public static ContentValues createContentValues(final BatteryInfoDTO batteryInfoDTO) {
			final ContentValues contentValues = new ContentValues();

			contentValues.put(COLUMN_CHARGING_STATE, batteryInfoDTO.getChargingState());
			contentValues.put(COLUMN_HEALTH, batteryInfoDTO.getHealth());
			contentValues.put(COLUMN_PERCENTAGE, batteryInfoDTO.getPercentage());
			contentValues.put(COLUMN_TEMPERATURE, batteryInfoDTO.getTemperature());
			contentValues.put(COLUMN_VOLTAGE, batteryInfoDTO.getVoltage());

			return contentValues;
		}

		/**
		 * Creates the {@link BatteryInfoDTO} object from the supplied cursor. The cursor should be properly positioned to the actual row.
		 * Else this method throws {@link IllegalStateException}.
		 * 
		 * @param cursor - the {@link Cursor} object
		 * @return {@link BatteryInfoDTO} object
		 */
		public static BatteryInfoDTO cursorToDTO(final Cursor cursor) {
			if (cursor.getCount() > 0 && !cursor.isBeforeFirst() && !cursor.isAfterLast()) {
				final BatteryInfoDTO dto = new BatteryInfoDTO();
				dto.setPercentage(cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.BatteryInfo.COLUMN_PERCENTAGE)));
				dto.setVoltage(cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.BatteryInfo.COLUMN_VOLTAGE)));
				dto.setChargingState(cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.BatteryInfo.COLUMN_CHARGING_STATE)));
				dto.setHealth(cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.BatteryInfo.COLUMN_HEALTH)));
				dto.setTemperature(cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.BatteryInfo.COLUMN_TEMPERATURE)));
				dto.setId(cursor.getLong(cursor.getColumnIndexOrThrow(STUNnerContract.BatteryInfo._ID)));
				return dto;
			}
			throw new IllegalStateException(CURSOR_POSITION_ERROR);
		}
	}

	/**
	 * Represents an entity to store the relevant mobile net information.
	 * 
	 * @author szelezsant
	 */
	public static final class MobileNetInfo implements BaseColumns {
		/** The constant content identifier. */
		public static final String TABLE_NAME = "mobilenetinfo";
		/** The content {@link Uri} pointing to the current content. */
		public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(TABLE_NAME).build();
		/** The MIME type for list queries. */
		public static final String CONTENT_TYPE_DIR = ContentResolver.CURSOR_DIR_BASE_TYPE + BASE_TABLE_MIME_TYPE + TABLE_NAME;
		/** The MIME type for single item queries. */
		public static final String CONTENT_TYPE_ITEM = ContentResolver.CURSOR_ITEM_BASE_TYPE + BASE_TABLE_MIME_TYPE + TABLE_NAME;

		/** Column to store the ssid of the network. */
		public static final String COLUMN_CARRIER = "carrier";
		/** Column to store the received signal strength index. */
		public static final String COLUMN_ISROAMING = "isroaming";
		/** Column to store the MAC address of the network adapter. */
		public static final String COLUMN_NETWORKTYPE = "networktype";

		/** The SQL command to create the table. */
		public static final String CREATE_TABLE = MessageFormat.format(DatabaseConstants.CREATE_TABLE_PATTERN, TABLE_NAME)
				+ "(" + _ID + " " + DatabaseConstants.INTEGER + " " + DatabaseConstants.PRIMARY_KEY + " " + DatabaseConstants.AUTOINCREMENT
				+ DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_CARRIER + " " + DatabaseConstants.TEXT + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_ISROAMING + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_NETWORKTYPE + " " + DatabaseConstants.TEXT + ");";

		/**
		 * Hidden constructor.
		 */
		private MobileNetInfo() {
		}

		/**
		 * Creates a {@link ContentValues} object from the given {@link MobileNetInfoDTO} object.
		 * 
		 * @param mobileNetInfoDTO - the {@link MobileNetInfoDTO} object representing the discovery.
		 * @return {@link ContentValues} object filled with the attributes and values of the argument
		 */
		public static ContentValues createContentValues(final MobileNetInfoDTO mobileNetInfoDTO) {
			final ContentValues contentValues = new ContentValues();

			contentValues.put(COLUMN_CARRIER, mobileNetInfoDTO.getCarrier());
			contentValues.put(COLUMN_ISROAMING, mobileNetInfoDTO.isRoaming() ? 1 : 0);
			contentValues.put(COLUMN_NETWORKTYPE, mobileNetInfoDTO.getNetworkType());

			return contentValues;
		}

		/**
		 * Creates the {@link MobileNetInfoDTO} object from the supplied cursor. The cursor should be properly positioned to the actual row.
		 * Else this method throws {@link IllegalStateException}.
		 * 
		 * @param cursor - the {@link Cursor} object
		 * @return {@link WifiInfoDTO} object
		 */
		public static MobileNetInfoDTO cursorToDTO(final Cursor cursor) {
			if (cursor.getCount() > 0 && !cursor.isBeforeFirst() && !cursor.isAfterLast()) {
				final MobileNetInfoDTO dto = new MobileNetInfoDTO();
				dto.setCarrier(cursor.getString(cursor.getColumnIndexOrThrow(STUNnerContract.MobileNetInfo.COLUMN_CARRIER)));
				dto.setNetworkType(cursor.getString(cursor.getColumnIndexOrThrow(STUNnerContract.MobileNetInfo.COLUMN_NETWORKTYPE)));
				dto.setRoaming(cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.MobileNetInfo.COLUMN_ISROAMING)) == 1);
				dto.setIdentifier(cursor.getLong(cursor.getColumnIndexOrThrow(STUNnerContract.MobileNetInfo._ID)));
				return dto;
			}
			throw new IllegalStateException(CURSOR_POSITION_ERROR);
		}
	}

	/**
	 * Represents an entity to store the relevant WiFi information.
	 * 
	 * @author szelezsant
	 */
	public static final class WifiInfo implements BaseColumns {
		/** The constant content identifier. */
		public static final String TABLE_NAME = "wifiinfo";
		/** The content {@link Uri} pointing to the current content. */
		public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(TABLE_NAME).build();
		/** The MIME type for list queries. */
		public static final String CONTENT_TYPE_DIR = ContentResolver.CURSOR_DIR_BASE_TYPE + BASE_TABLE_MIME_TYPE + TABLE_NAME;
		/** The MIME type for single item queries. */
		public static final String CONTENT_TYPE_ITEM = ContentResolver.CURSOR_ITEM_BASE_TYPE + BASE_TABLE_MIME_TYPE + TABLE_NAME;

		/** Column to store the ssid of the network. */
		public static final String COLUMN_SSID = "ssid";
		/** Column to store bandwidth. */
		public static final String COLUMN_BANDWIDTH = "bandwidth";
		/** Column to store the received signal strength index. */
		public static final String COLUMN_RSSI = "rssi";
		/** Column to store the MAC address of the network adapter. */
		public static final String COLUMN_MACADDRESS = "macaddress";

		/** The SQL command to create the table. */
		public static final String CREATE_TABLE = MessageFormat.format(DatabaseConstants.CREATE_TABLE_PATTERN, TABLE_NAME)
				+ "(" + _ID + " " + DatabaseConstants.INTEGER + " " + DatabaseConstants.PRIMARY_KEY + " " + DatabaseConstants.AUTOINCREMENT
				+ DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_SSID + " " + DatabaseConstants.TEXT + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_BANDWIDTH + " " + DatabaseConstants.TEXT + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_RSSI + " " + DatabaseConstants.INTEGER + DatabaseConstants.COMMA_SEPARATOR
				+ COLUMN_MACADDRESS + " " + DatabaseConstants.TEXT + ");";

		/**
		 * Hidden constructor.
		 */
		private WifiInfo() {
		}

		/**
		 * Creates a {@link ContentValues} object from the given {@link WifiInfoDTO} object.
		 * 
		 * @param wifiInfoDTO - the {@link WifiInfoDTO} object representing the discovery.
		 * @return {@link ContentValues} object filled with the attributes and values of the argument
		 */
		public static ContentValues createContentValues(final WifiInfoDTO wifiInfoDTO) {
			final ContentValues contentValues = new ContentValues();

			contentValues.put(COLUMN_SSID, wifiInfoDTO.getSsid());
			contentValues.put(COLUMN_RSSI, wifiInfoDTO.getRssi());
			contentValues.put(COLUMN_BANDWIDTH, wifiInfoDTO.getBandwidth());
			contentValues.put(COLUMN_MACADDRESS, wifiInfoDTO.getMacAddress());

			return contentValues;
		}

		/**
		 * Creates the {@link WifiInfoDTO} object from the supplied cursor. The cursor should be properly positioned to the actual row. Else
		 * this method throws {@link IllegalStateException}.
		 * 
		 * @param cursor - the {@link Cursor} object
		 * @return {@link WifiInfoDTO} object
		 */
		public static WifiInfoDTO cursorToDTO(final Cursor cursor) {
			if (cursor.getCount() > 0 && !cursor.isBeforeFirst() && !cursor.isAfterLast()) {
				final WifiInfoDTO dto = new WifiInfoDTO();
				dto.setSsid(cursor.getString(cursor.getColumnIndexOrThrow(STUNnerContract.WifiInfo.COLUMN_SSID)));
				dto.setBandwidth(cursor.getString(cursor.getColumnIndexOrThrow(STUNnerContract.WifiInfo.COLUMN_BANDWIDTH)));
				dto.setRssi(cursor.getInt(cursor.getColumnIndexOrThrow(STUNnerContract.WifiInfo.COLUMN_RSSI)));
				dto.setMacAddress(cursor.getString(cursor.getColumnIndexOrThrow(STUNnerContract.WifiInfo.COLUMN_MACADDRESS)));
				dto.setIdentifier(cursor.getLong(cursor.getColumnIndexOrThrow(STUNnerContract.WifiInfo._ID)));
				return dto;
			}
			throw new IllegalStateException(CURSOR_POSITION_ERROR);
		}
	}
}